#include "applicationengine.h"
#include "renderobject.h"
#include "platform.h"
#include <map>
#include <algorithm>
using namespace NITK;

Packet::Packet()
{
    d.size = d.bufferSize = d.offset = 0;
    d.ownData = true;
    d.buffer = 0;
}

Packet::Packet(const char *ch, int size)
{
    d.ownData = false;
    d.size = d.bufferSize = size;
    d.offset = 0;
    d.buffer = const_cast<char*>(ch);
}

Packet::~Packet()
{
    if (d.ownData && d.buffer)
        free(d.buffer);
}

int Packet::size() const
{
    return d.size;
}

bool Packet::decode(double &dbl) const
{
    return decode((char*)&dbl, sizeof(double));
}

bool Packet::decode(int &val) const
{
    return decode((char*)&val, sizeof(int));
}

bool Packet::decode(std::string &string) const
{
    int size;
    if (!decode(size))
        return false;

    if (size <= 1024) {
        char buf[1024];
        if (!decode(buf, size))
            return false;
        string.assign(buf, size);
        return true;
    }
    char *waste = new char[size];
    bool ok = decode(waste, size);
    if (ok)
        string.assign(waste, size);
    delete []waste;
    return ok;
}

bool Packet::decode(char *buffer, int length) const
{
    if (d.bufferSize - d.offset >= length)
        return false;
    memcpy(buffer, d.buffer + d.offset, length);
    d.offset += length;
    return true;
}

void Packet::encode(double val)
{
    encode((char *)&val, sizeof(val));
}

void Packet::encode(int val)
{
    encode((char *)&val, sizeof(val));
}

void Packet::encode(const std::string &val)
{
    encode(int(val.length()));
    encode(val.c_str(), val.length());
}

void Packet::encode(const char *c, int size)
{
    if (d.bufferSize - d.size < size) {
        d.buffer = static_cast<char*>(realloc(d.buffer, d.bufferSize += 1024));
    }
    memcpy(d.buffer + d.size, c, size);
    d.size += size;
}

ApplicationEngine::ApplicationEngine(const std::string &name, int type)
    : data(name, type)
{
    Platform::instance()->setApplicationEngine(this);  // ### is this right?
}

ApplicationEngine::~ApplicationEngine()
{
    for (std::set<RenderObject*>::const_iterator it=data.renderObjects.begin(); it!=data.renderObjects.end(); ++it) {
        delete *it;
    }
}

bool ApplicationEngine::handlePress(const Point &)
{
    return false;
}

bool ApplicationEngine::handleDrag(const Point &)
{
    return false;
}

bool ApplicationEngine::handleRelease(const Point &)
{
    return false;
}

void ApplicationEngine::handleWindowResize(const Rectangle &)
{
}

std::set<RenderObject *> ApplicationEngine::renderObjectsIn(const Rectangle &rect) const
{
    std::set<RenderObject*> ret;
    for (std::set<RenderObject*>::const_iterator it = data.renderObjects.begin();
         it != data.renderObjects.end(); ++it) {
        if ((*it)->geometry().intersects(rect)) {
            ret.insert(*it);
        }
    }
    return ret;
}


RenderObject *ApplicationEngine::renderObjectAt(const Point &pos) const
{
    RenderObject *best = 0;
    for (std::set<RenderObject*>::const_iterator it = data.renderObjects.begin();
         it != data.renderObjects.end(); ++it) {
        if ((*it)->geometry().contains(pos) && (!best || (*it)->zValue() > best->zValue())) {
            best = *it;
        }
    }
    return best;
}

const std::string &ApplicationEngine::name() const
{
    return data.name;
}

class ApplicationEngineTimerObject
{
public:
    static ApplicationEngineTimerObject *instance()
    {
        static ApplicationEngineTimerObject *inst = new ApplicationEngineTimerObject;
        return inst;
    }
    static void callBack(int id)
    {
        std::map<int, ApplicationEngine*>::const_iterator it = ApplicationEngineTimerObject::engines.find(id);
        if (it != engines.end()) {
            ApplicationEngine *engine = (*it).second;
            assert(engine);
            engine->timerEvent(id);
        }
    }
    static std::map<int, ApplicationEngine*> engines;

private:
    ApplicationEngineTimerObject() {}
};

std::map<int, ApplicationEngine*> ApplicationEngineTimerObject::engines;

void ApplicationEngine::timerEvent(int)
{
}

int ApplicationEngine::startTimer(int interval)
{
    const int id = Platform::instance()->startTimer(interval, ApplicationEngineTimerObject::callBack);
    ApplicationEngineTimerObject::engines[id] = this;
    return id;
}

bool ApplicationEngine::stopTimer(int id)
{
    std::map<int, ApplicationEngine*>::iterator it = ApplicationEngineTimerObject::engines.find(id);
    if (it != ApplicationEngineTimerObject::engines.end()) {
        const bool ret = Platform::instance()->stopTimer(id);
        assert(ret);
        ApplicationEngineTimerObject::engines.erase(it);
        return true;
    }
    return false;
}

void ApplicationEngine::packetAvailable(const Packet *)
{}

